package crypto

import (
	"math/big"
	"reflect"

	"crypto/ecdsa"
	"crypto/ed25519"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/sha256"

	"golang.org/x/crypto/bn256"
	"golang.org/x/crypto/curve25519"
)

func NewEcc() *ecc {
	// key, err := hex.DecodeString("0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20")
	// if err != nil {
	// 	panic(err)
	// }
	// copy(data.key[:], key)

	sk, pk, err := bn256.RandomG2(rand.Reader)
	if err != nil {
		panic(err)
	}

	publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader)
	if err != nil {
		panic(err)
	}

	// seed := make([]byte, ed25519.SeedSize)
	// _, err := io.ReadFull(rand.Reader, seed)
	// if err != nil {
	// 	panic(err)
	// }
	//
	// privateKey := ed25519.NewKeyFromSeed(seed)
	// publicKey := make([]byte, ed25519.PublicKeySize)
	// copy(publicKey, privateKey[32:])

	// elliptic.P224()
	// elliptic.P256()
	// elliptic.P384()
	// elliptic.P521()
	// elliptic.Marshal()
	// elliptic.Unmarshal()
	// elliptic.MarshalCompressed()
	// elliptic.UnmarshalCompressed()
	// ellipticPrivateKey, x, y err := elliptic.GenerateKey(elliptic.P256(), rand.Reader)

	ecdsaPrivateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		panic(err)
	}

	return &ecc{
		// key:             key,
		sk:              sk,
		pk:              pk,
		publicKey:       publicKey,
		privateKey:      privateKey,
		ecdsaPrivateKey: ecdsaPrivateKey,
	}
}

type ecc struct {
	// key            *[32]byte
	sk              *big.Int
	pk              *bn256.G2
	privateKey      ed25519.PrivateKey
	publicKey       ed25519.PublicKey
	ecdsaPrivateKey *ecdsa.PrivateKey
}

func hashToG1(source []byte) *bn256.G1 {
	hash := NewHash().Sha256(source)
	tmp := new(big.Int).SetBytes(hash)
	return new(bn256.G1).ScalarBaseMult(tmp)
}

// Bn256
// sk
// pk
func (r *ecc) Bn256(source []byte) *bn256.G1 {
	hash := hashToG1(source)
	return new(bn256.G1).ScalarMult(hash, r.sk)
}
func (r *ecc) Bn256Verify(source []byte, sig *bn256.G1) bool {
	hash := hashToG1(source)
	rp := bn256.Pair(hash, r.pk)

	g2 := new(bn256.G2).ScalarBaseMult(big.NewInt(1))
	lp := bn256.Pair(sig, g2)

	return reflect.DeepEqual(lp.Marshal(), rp.Marshal())
}

// Curve25519 todo
func (r *ecc) Curve25519() []byte {
	result := make([]byte, 32)

	result, err := curve25519.X25519(result, curve25519.Basepoint)
	// var dst, src, point [32]byte
	// curve25519.ScalarBaseMult(&dst, &src)
	// curve25519.ScalarMult(&dst, &src, &point)
	// copy(result, dst[:])
	if err != nil {
		panic(err)
	}

	return result
}

// Ecdsa
// ecdsaPrivateKey
func (r *ecc) Ecdsa(message []byte) (*big.Int, *big.Int) {
	hash := sha256.Sum256(message)

	r1, s, err := ecdsa.Sign(rand.Reader, r.ecdsaPrivateKey, hash[:])
	if err != nil {
		panic(err)
	}

	return r1, s
}
func (r *ecc) EcdsaVerify(message []byte, r1, s *big.Int) bool {
	hash := sha256.Sum256(message)

	return ecdsa.Verify(&r.ecdsaPrivateKey.PublicKey, hash[:], r1, s)
}
func (r *ecc) EcdsaASN1(message []byte) []byte {
	hash := sha256.Sum256(message)

	r1, err := ecdsa.SignASN1(rand.Reader, r.ecdsaPrivateKey, hash[:])
	if err != nil {
		panic(err)
	}

	return r1
}
func (r *ecc) EcdsaASN1Verify(message []byte, r1 []byte) bool {
	hash := sha256.Sum256(message)

	return ecdsa.VerifyASN1(&r.ecdsaPrivateKey.PublicKey, hash[:], r1)
}

// Ed25519
// privateKey
// publicKey
func (r *ecc) Ed25519(message []byte) []byte {
	return ed25519.Sign(r.privateKey, message)
}
func (r *ecc) Ed25519Verify(message, signature []byte) bool {
	return ed25519.Verify(r.publicKey, message, signature)
}
